/*
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.drawee.controller;

import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

import java.util.List;

import android.graphics.drawable.Animatable;
import android.util.Log;

import com.facebook.common.internal.Lists;

/**
 * Listener that forwards controller events to multiple listeners.
 */
@ThreadSafe
public class ForwardingControllerListener<INFO> implements ControllerListener<INFO> {
    private static final String TAG = "ForwardingControllerListener";

    private final List<ControllerListener<? super INFO>> mListeners
            = Lists.newArrayListWithCapacity(2);

    public ForwardingControllerListener() {
    }

    public static <INFO> ForwardingControllerListener<INFO> create() {
        return new ForwardingControllerListener<INFO>();
    }

    public static <INFO> ForwardingControllerListener<INFO> of(
            ControllerListener<? super INFO> listener) {
        ForwardingControllerListener<INFO> forwarder = create();
        forwarder.addListener(listener);
        return forwarder;
    }

    public static <INFO> ForwardingControllerListener<INFO> of(
            ControllerListener<? super INFO> listener1,
            ControllerListener<? super INFO> listener2) {
        ForwardingControllerListener<INFO> forwarder = create();
        forwarder.addListener(listener1);
        forwarder.addListener(listener2);
        return forwarder;
    }

    public synchronized void addListener(ControllerListener<? super INFO> listener) {
        mListeners.add(listener);
    }

    public synchronized void removeListener(ControllerListener<? super INFO> listener) {
        mListeners.remove(listener);
    }

    public synchronized void clearListeners() {
        mListeners.clear();
    }

    private synchronized void onException(String message, Throwable t) {
        Log.e(TAG, message, t);
    }

    @Override
    public synchronized void onSubmit(String id, Object callerContext) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onSubmit(id, callerContext);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onSubmit", exception);
            }
        }
    }

    @Override
    public synchronized void onFinalImageSet(
            String id,
            @Nullable INFO imageInfo,
            @Nullable Animatable animatable) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onFinalImageSet(id, imageInfo, animatable);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onFinalImageSet", exception);
            }
        }
    }

    @Override
    public void onIntermediateImageSet(String id, @Nullable INFO imageInfo) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onIntermediateImageSet(id, imageInfo);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onIntermediateImageSet", exception);
            }
        }
    }

    @Override
    public void onIntermediateImageFailed(String id, Throwable throwable) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onIntermediateImageFailed(id, throwable);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onIntermediateImageFailed", exception);
            }
        }
    }

    @Override
    public synchronized void onFailure(String id, Throwable throwable) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onFailure(id, throwable);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onFailure", exception);
            }
        }
    }

    @Override
    public synchronized void onRelease(String id) {
        final int numberOfListeners = mListeners.size();
        for (int i = 0; i < numberOfListeners; ++i) {
            ControllerListener<? super INFO> listener = mListeners.get(i);
            try {
                listener.onRelease(id);
            } catch (Exception exception) {
                // Don't punish the other listeners if we're given a bad one.
                onException("InternalListener exception in onRelease", exception);
            }
        }
    }
}
